package com.ccf.business.knowledge.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ccf.business.knowledge.model.*;
import com.ccf.business.knowledge.model.vo.LabelStatisticalVO;
import com.ccf.business.knowledge.model.vo.LabelTaskVO;
import com.ccf.business.knowledge.service.*;
import com.google.common.collect.Lists;
import com.common.utils.constant.Constants;
import com.common.utils.tool.UserUtil;
import com.ccf.business.knowledge.client.SourceClient;
import com.ccf.business.knowledge.mapper.LabelTaskMapper;
import com.ccf.business.knowledge.model.*;
import com.ccf.business.knowledge.model.vo.RelationTypeVO;
import com.ccf.business.knowledge.service.*;
import com.common.link.cache.AtlasCache;
import com.common.link.cache.AtlasCacheService;
import com.common.link.model.EntityVO;
import com.common.model.pojo.Order;
import com.common.model.pojo.Query;
import com.common.model.result.CodeEnum;
import com.common.model.result.PageResult;
import com.common.model.pojo.DelFlag;
import com.service.mybatis.service.impl.SuperFlagServiceImpl;
import com.service.mybatis.service.impl.SuperServiceImpl;
import com.service.mybatis.util.Condition;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.MDC;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 标注任务
 *
 * @author ccf
 * @date 2022-02-15 16:56:10
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class LabelTaskServiceImpl extends SuperFlagServiceImpl<LabelTaskMapper, LabelTask> implements ILabelTaskService {

    private ILabelDataService labelDataService;

    @Autowired
    public void setLabelDataService(ILabelDataService labelDataService) {
        this.labelDataService = labelDataService;
    }

    private final ILabelTaskTypeService iLabelTaskTypeService;

    private final ILabelDataEntityService iLabelDataEntityService;

    private final ILabelDataRelationService iLabelDataRelationService;

    private final ILabelTaskUserService iLabelTaskUserService;

    private final AtlasCacheService atlasCacheService;

    private final SourceClient sourceClient;

    /**
    * 列表
    * @param query
    * @return
    */
    @Override
    public PageResult<LabelTaskVO> findList(Query query){
        query.addDelFlag();
        Boolean fileNumOrder = query.judgeOrderByField("fileNum", true);
        query.convert(LabelTaskVO.class);
        QueryWrapper<LabelTask> queryWrapper =
                Condition.getQueryWrapperAndUserName(query, "creator");
        Page<LabelTask> page = page(Condition.getPage(query), queryWrapper);
        List<LabelTaskVO> collect = page.getRecords().stream()
                .map(e -> new LabelTaskVO(e, labelDataService))
                .collect(Collectors.toList());
        if (fileNumOrder != null){
            collect = collect.stream().sorted(query.getNumberComparator(fileNumOrder, LabelTaskVO::getFileNum)).collect(Collectors.toList());
        }
        return PageResult.<LabelTaskVO>builder()
                .datas(collect)
                .code(CodeEnum.SUCCESS.getCode())
                .count(page.getTotal()).build();
    }


    private Boolean getIsFileNumOrder(Query query) {
        // fileNum 特殊处理
        Boolean fileNumOrder = null;
        Order fileNum = query.getOrderByFiledName("fileNum");
        if (fileNum != null){
            fileNumOrder = fileNum.getAsc();
        }
        return fileNumOrder;
    }

    private Comparator<LabelTaskVO> getLabelTaskVOComparator(final Boolean fileNumOrder) {
        return (a,b)->
        fileNumOrder?a.getFileNum() - b.getFileNum() : b.getFileNum() - a.getFileNum();
    }


    @Override
    public PageResult labelList(Query query) {
        query.addDelFlag();
        //特殊处理  这句代码必须在convert之前
        Boolean fileNumOrder = getIsFileNumOrder(query);
        query.convert(LabelTaskVO.class);
        final String userId = MDC.get(Constants.USER_ID);
        QueryWrapper<LabelTask> queryWrapper = getLabelTaskQueryWrapper(query, userId);
        Page<LabelTask> page = page(Condition.getPage(query), queryWrapper);
        List<LabelTaskVO> collect = page.getRecords().stream().map(e ->
                new LabelTaskVO(e, labelDataService, userId))
                .collect(Collectors.toList());
        if (fileNumOrder != null){
            collect = collect.stream().sorted(getLabelTaskVOComparator(fileNumOrder)).collect(Collectors.toList());
        }
        return PageResult.<LabelTaskVO>builder()
                .datas(collect)
                .code(CodeEnum.SUCCESS.getCode())
                .count(page.getTotal()).build();
    }

    /**
     * 添加用户条件
     * @param query
     * @param userId
     * @return
     */
    private QueryWrapper<LabelTask> getLabelTaskQueryWrapper(Query query, String userId) {
        List<LabelTaskUser> userList = iLabelTaskUserService.list(Wrappers.<LabelTaskUser>query()
                .lambda().in(LabelTaskUser::getUserId, userId));
        Set<String> taskIds = userList.stream().map(e -> e.getTaskId()).collect(Collectors.toSet());
        QueryWrapper<LabelTask> queryWrapper = Condition.getQueryWrapper(query);
        queryWrapper.gt("status", LabelTask.WAIT);
        if (CollUtil.isNotEmpty(taskIds)){
            queryWrapper.in("id", taskIds);
        } else {
            queryWrapper.eq("1", "2");
        }
        return queryWrapper;
    }

    @Override
    public PageResult auditList(Query query) {
        query.addDelFlag();
        Boolean fileNumOrder = getIsFileNumOrder(query);
        query.convert(LabelTaskVO.class);
        QueryWrapper<LabelTask> queryWrapper = Condition.getQueryWrapper(query);
        queryWrapper.gt("status", LabelTask.TAGGING);
        Page<LabelTask> page = page(Condition.getPage(query), queryWrapper);
        List<LabelTaskVO> collect = page.getRecords().stream().map(e -> new LabelTaskVO(e, labelDataService)).collect(Collectors.toList());
        if (fileNumOrder != null){
            collect = collect.stream().sorted(getLabelTaskVOComparator(fileNumOrder)).collect(Collectors.toList());
        }
        return PageResult.<LabelTaskVO>builder()
                .datas(collect)
                .code(CodeEnum.SUCCESS.getCode())
                .count(page.getTotal()).build();
    }

    /**
     * 保存所有
     * 1.保存文件
     * 2.保存标注人
     * 3.保存实体或关系
     * 4.保存自身
     * @param labelTask
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public String saveOrUpdateAll(LabelTaskVO labelTask) {
        boolean isUpdate = false;
        //更新不验证
        if (StrUtil.isNotBlank(labelTask.getId())){
            LabelTask byId = getById(labelTask.getId());
            if (byId == null){
               throw new RuntimeException("更新失败，id不存在");
            }
            BeanUtil.copyProperties(labelTask, byId, "creator", "create_time", "update_time");
            //保存任务
            byId.setStatus(LabelTask.WAIT);
            saveOrUpdate(byId);
            isUpdate = true;
        } else {
            //验证
            validation(labelTask);
            //保存任务
            labelTask.setStatus(LabelTask.WAIT);
            saveOrUpdate(labelTask);
        }
        String id = labelTask.getId();
        String[] files = labelTask.getFileList();
        String[] users = labelTask.getUserList();
        if (files != null){
            //保存数据
            labelDataService.saveDatas(labelTask, files, users, isUpdate);
        }
        if (users != null) {
            //保存标注人
            iLabelTaskUserService.saveUser(id, users, isUpdate);
        }
        //保存类型
        iLabelTaskTypeService.saveTypes(labelTask, isUpdate);

        return id;
    }

    /**
     * 验证完整性
     * @param labelTask
     */
    private void validation(LabelTaskVO labelTask) {

        String[] userList = labelTask.getUserList();
        if (ArrayUtil.isEmpty(userList)){
            throw new RuntimeException("标注人不能为空");
        }


        String[] fileList = labelTask.getFileList();
        if (ArrayUtil.isEmpty(fileList)){
            throw new RuntimeException("未选择标注数据");
        }

        //是否是实体标注
        if (LabelTask.ENTITY.equalsIgnoreCase(labelTask.getLabelType())){
            String[] entityList = labelTask.getEntityList();
            if (ArrayUtil.isEmpty(entityList)){
                throw new RuntimeException("未选择标注实体类型");
            }
        } else {
            RelationTypeVO[] relationList = labelTask.getRelationList();
            if (ArrayUtil.isEmpty(relationList)){

                throw new RuntimeException("未选择标注关系类型");
            }
        }
    }

    @Override
    public void submit(String id) {
       change(id, LabelTask.TAGGING);
    }

    @Override
    public void change(String id, Integer status) {
        LabelTask byId = getById(id);
        byId.setStatus(status);
        saveOrUpdate(byId);
    }

    @Override
    public void logicRemoveByIds(String... ids) {
        List<LabelTask> oldDatas = list(Wrappers.<LabelTask>query()
                .lambda().in(LabelTask::getId, ids));
        if (CollUtil.isNotEmpty(oldDatas)){
            oldDatas.stream().forEach(e->e.setDelFlag(DelFlag.NOT_EXIST.getCode()));
            saveOrUpdateBatch(oldDatas);
        }
    }

    @Override
    public LabelTaskVO detail(String id) {
        LabelTask labelTask = getById(id);
        if (labelTask == null){
            throw new RuntimeException("当前id不存在");
        }
        LabelTaskVO labelTaskVO = new LabelTaskVO(labelTask, labelDataService);
        List<LabelTaskUser> list = iLabelTaskUserService.list(Wrappers.<LabelTaskUser>query()
                .lambda().eq(LabelTaskUser::getTaskId, id));
        labelTaskVO.setUserList(list.stream().map(e->e.getUserId())
                .collect(Collectors.toList()).toArray(new String[0]));

        labelTaskVO.setUserListStr(list.stream().map(e-> UserUtil.get(e.getUserId())).collect(Collectors.joining(Constants.COMMA)));
        List<LabelData> datas = labelDataService.list(Wrappers.<LabelData>query()
                .lambda()
                .select(LabelData::getId,
                        LabelData::getTaskId,
                        LabelData::getSourceId,
                        LabelData::getAuditor,
                        LabelData::getAuditTime,
                        LabelData::getTager,
                        LabelData::getTagTime,
                        LabelData::getStatus
                        )
                .eq(LabelData::getTaskId, id));
        Map<String, String> nameMap = sourceClient.getNameMap();
        for (LabelData data : datas) {
            String sourceId = data.getSourceId();
            String fileName = nameMap.get(sourceId);
            data.setFileName(fileName);
        }
        labelTaskVO.setFileDatas(datas);

        //标记文件
        if (CollUtil.isNotEmpty(datas)){
            List<String> fileIds = datas.stream().map(e -> e.getId()).collect(Collectors.toList());
            statistical(labelTask, labelTaskVO, fileIds);
        }
        labelTaskVO.setEntityDatas(iLabelTaskTypeService.entityList(id));
        labelTaskVO.setRelationDatas(iLabelTaskTypeService.relationList(id, null, null));
        return labelTaskVO;
    }

    private void statistical(LabelTask labelTask, LabelTaskVO labelTaskVO, List<String> fileIds) {
        List<LabelStatisticalVO> statistical = Lists.newArrayList();
        if (LabelTask.ENTITY.equalsIgnoreCase(labelTask.getLabelType())){
            List<LabelDataEntity> entities =
                    iLabelDataEntityService.list(Wrappers.<LabelDataEntity>query()
                            .lambda().in(LabelDataEntity::getDataId, fileIds));
            Map<String, List<LabelDataEntity>> numMap =
                    entities.stream().collect(Collectors.groupingBy(LabelDataEntity::getTypeId));
            for (Map.Entry<String, List<LabelDataEntity>> entry : numMap.entrySet()) {
                String key = entry.getKey();
                List<LabelDataEntity> value = entry.getValue();
                EntityVO entityVo = atlasCacheService.getEntityById(key);
                statistical.add(new LabelStatisticalVO(key,
                        Optional.ofNullable(entityVo).map(EntityVO::getName).orElse(null), value.size()));
            }

        } else {
            List<LabelDataRelation> relations =
                    iLabelDataRelationService.list(Wrappers.<LabelDataRelation>query()
                            .lambda().in(LabelDataRelation::getDataId, fileIds));
            Map<String, List<LabelDataRelation>> numMap =
                    relations.stream().collect(Collectors.groupingBy(LabelDataRelation::getTypeId));
            for (Map.Entry<String, List<LabelDataRelation>> entry : numMap.entrySet()) {
                String key = entry.getKey();
                List<LabelDataRelation> value = entry.getValue();
                statistical.add(new LabelStatisticalVO(key,
                        AtlasCache.get(key), value.size()));
            }
        }
        labelTaskVO.setStatistical(statistical);
    }

    @Override
    public void dataTidy(String id) {
        LabelTask byId = getById(id);
        String labelType = byId.getLabelType();
        List<LabelData> list = labelDataService.list(Wrappers.<LabelData>query()
                .lambda().eq(LabelData::getTaskId, id));
        if (LabelTask.ENTITY.equalsIgnoreCase(labelType)){
            for (LabelData labelData : list) {
                String path = iLabelDataEntityService.entityTidy(labelData.getId(), labelData.getData());
                labelData.setPath(path);
            }
        } else {
            for (LabelData labelData : list) {
                String path = iLabelDataRelationService.relationTidy(labelData.getId(), labelData.getData());
                labelData.setPath(path);
            }
        }
        labelDataService.saveOrUpdateBatch(list);
    }
}
